Typically, cross-platform software is compiled into an intermediate code, which may be translated into native machine code and subsequently executed, by a platform-specific execution engine, or virtual machine (VM). However convenient, the intermediate step of instruction-by-instruction interpretation into native code may impose substantial performance penalties, relative to purely native code execution. For example, many languages employ a stack-based VM that generates and stores a frame upon each method invocation to preserve its pre-invocation state. A frame may include an operand stack, an array of local variables, and a reference to a related pool of constants. After each method completes, the VM recoups the respective state data, and releases the frame. Thus, a sequence that invokes multiple methods, nested methods, or loops having complex method sequences, may result in significant performance-draining overhead. An unconstrained resource platform may have sufficient processor speed, memory, available power, or other platform resources, so that program execution by exclusive interpretation produces little perceptible performance degradation. However, in a constrained resource platform, exclusive interpretation may impose an unacceptably high cost, high power consumption, and reduced performance.
In an effort to reduce interpretation, a VM may include a just-in-time (JIT), or dynamic, compiler that translates particular sequences into native machine code, on-the-fly, prior to, or during, direct execution by the underlying physical CPU. Nevertheless, dynamic compilation techniques may impose significant costs during execution of an application. For example, where an instruction sequence invokes multiple methods, each method may be compiled in its entirety. As a result, dynamic compilation may result in a delay, or latency, which may extend from the beginning of compilation until the onset of the underlying system response to the executed code. This latency can be significant when compiling complex sequences, and may be undesirable for time-sensitive applications. In another example, dynamic dispatching is a costly runtime technique used to determine a particular method code invocation sequence, which may not be known at compile time. As a result, current dynamic compilers can do little to ameliorate overhead which may be associated with dynamic dispatching. Also, compiled method code may be stored in a code cache for future execution, potentially reducing future latency. Over time, a dynamic compiler may compile and store even rarely-used methods encountered during VM operation, which may represent a suboptimal use of platform resources.
On an unconstrained platform, many current dynamic compilation techniques may produce optimized native code transparently, despite consuming significant platform resources. However, the platform resources used to dynamically compile optimized native code may be far beyond those available in many embedded computers, for example, on an inexpensive mobile telephone using an embedded processor. In an attempt to strike a balance between resource consumption and compiled code performance, embedded dynamic compilers have evolved, using simple algorithms and heuristics, which may not yield sufficient code optimization and, ultimately, execution performance, particularly for sophisticated embedded applications. In view of the foregoing, there is a need for a dynamic compiler capable of producing optimized native code that may be suitable for use in a constrained-resource platform, including an embedded system.